﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Web;
using Microsoft.Security.Application;
using SharpArch.Core;

namespace good1.Web.Controllers.Infrastructure.Cookies
{
    public class CookieContainer : ICookieContainer
    {
        private readonly HttpRequestBase request;
        private readonly HttpResponseBase response;
        private readonly HttpContextBase context;

        public CookieContainer()
        {
            var httpContext = new HttpContextWrapper(HttpContext.Current);
            request = httpContext.Request;
            response = httpContext.Response;
            context = new HttpContextWrapper(HttpContext.Current);

        }

        public CookieContainer(HttpRequestBase request, HttpResponseBase response)
        {
            Check.Require(request != null, "request cannot be null");
            Check.Require(response != null, "response cannot be null");

            this.request = request;
            this.response = response;
        }

        #region ICookieContainer Members

        public bool Exists(string key)
        {
            Check.Require(!string.IsNullOrEmpty(key), "key cannot be null or empty");

            return request.Cookies[key] != null;
        }

        public string GetValue(string key)
        {
            Check.Require(!string.IsNullOrEmpty(key), "key cannot be null or empty");

            HttpCookie cookie = request.Cookies[key];
            return cookie != null ? AntiXss.HtmlEncode(cookie.Value) : null;
        }

        public T GetValue<T>(string key)
        {
            string val = GetValue(key);

            if (val == null)
                return default(T);

            Type type = typeof(T);
            bool isNullable = type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
            if (isNullable)
                type = new NullableConverter(type).UnderlyingType;
            
            return (T)Convert.ChangeType(val, type, CultureInfo.InvariantCulture);
        }

        public void SetValue(string key, object value, DateTime expires,bool insideHttpPostEvent)
        {
            Check.Require(!string.IsNullOrEmpty(key), "key cannot be null or empty");

            string strValue = CheckAndConvertValue(value);

            HttpCookie cookie = new HttpCookie(key, strValue) { Expires = expires, HttpOnly = true};
            
            if(insideHttpPostEvent)
            {
                request.Cookies.Set(cookie);
            }
            else
            {
                response.Cookies.Set(cookie);
            }
        }

        public void SetValue(string key, object value, DateTime expires)
        {
            SetValue(key, value, expires, false);
        }

        public void AddValue(string key, object value, DateTime expires)
        {
            Check.Require(!string.IsNullOrEmpty(key), "key cannot be null or empty");

            string strValue = CheckAndConvertValue(value);

            HttpCookie cookie = new HttpCookie(key, strValue) { Expires = expires };

            response.Cookies.Add(cookie);
    }

        public void AddCookie(HttpCookie cookie)
        {
            cookie.HttpOnly = true;  //http://www.codinghorror.com/blog/archives/001167.html
            //n.b. be damned if we can do this without HttpContext
            //to quote ScottHa "hate to do it this way, but Response.Cookies isn't implemented
            //in Controller.HttpContextBase.Response"
            HttpContext.Current.Response.Cookies.Add(cookie);
        }

        #endregion

        private static string CheckAndConvertValue(object value)
        {
            if (value == null)
                return null;

            if (value is string)
                return value.ToString();

            // only allow value types and nullable<value types>

            Type type = value.GetType();
            bool isTypeAllowed = false;

            if (type.IsValueType)
                isTypeAllowed = true;
            else
            {
                bool isNullable = type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
                if (isNullable)
                {
                    NullableConverter converter = new NullableConverter(type);
                    Type underlyingType = converter.UnderlyingType;
                    if (underlyingType.IsValueType)
                        isTypeAllowed = true;
                }
            }

            if (!isTypeAllowed)
                throw new NotSupportedException("Only value types and Nullable<ValueType> are allowed!");

            return (string)Convert.ChangeType(value, typeof(string), CultureInfo.InvariantCulture);
        }
    }
}
